package com.hsc.mvc;

import com.hsc.mvc.bind.EntityParameterBinder;
import com.hsc.mvc.bind.NormalParameterBinder;
import com.hsc.mvc.bind.ParameterBindStrategy;
import com.hsc.mvc.bind.ServletParameterBinder;
import com.hsc.mvc.config.AnnotationReader;
import com.hsc.mvc.config.ClasspathXMLReader;
import com.hsc.mvc.config.RemoteXMLReader;
import com.hsc.mvc.config.SystemXMLReader;
import com.hsc.mvc.conversion.ConversionStretagy;
import com.hsc.mvc.conversion.Convertor;
import com.hsc.mvc.exceptions.RepeatMappingException;
import com.hsc.mvc.handler.ResponseHandleStrategy;
import com.hsc.mvc.tag.AopMappingTag;
import com.hsc.mvc.tag.ConvertorTag;
import com.hsc.mvc.tag.MappingTag;
import com.hsc.mvc.tag.ParamBinderTag;
import com.hsc.mvc.util.ConversionUtil;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.util.*;

/**
 * @author
 * @date 2025-05-18
 * @Description 配置类 用于框架读取配置文件
 */
public class Configuration {
    /**
     * 如何可以快速的根据url找到对应的目标？ 使用map
     * 用于保存映射关系
     * 存储所有的mappingTags （目标映射）
     * 一个url请求，可以对应几个目标?  1个
     */
    HashMap<String, MappingTag> mappingMap = new HashMap<String, MappingTag>();

    /**
     * 单实例对象容器存储的是目标方法
     */
    private Map<Class, Object> singleObjectMap = new HashMap<>();
    /**
     * 存储所有的aopMappingTags
     * 使用ArrayList的原因是 在未来可能一个请求 会经过多个拦截器
     * 所有我们为了遍历 取出多个数据  使用ArrayList
     */
    ArrayList<AopMappingTag> aopMappingTagList = new ArrayList<>();

    /**
     * 存储自定义xml文件中的的参数绑定器 这里只是xml文件的对应的Class 对象
     */
    List<ParamBinderTag> paramBinderTags = new ArrayList<>();

    /**
     * 这里保存了所有的参数绑定器对象 包括自定义的参数绑定器 以及系统内置的参数绑定器
     * 这为何使用List? 由于参数绑定器会有多个 但是传递参数时候 我们并不知道用户写大代码 需要什么样的类型的参数 所以我们使用List
     * 当用户传递写好的代码 我们进行参数绑定时 进行遍历
     */
    List<ParameterBindStrategy> binders = new ArrayList<>();

    /**
     * key = 左侧数据类型 与 右侧目标类型的一个字符串组合
     * java.lang.String[]-java.lang.String
     * <p>
     * value：拿到是具体的策略类
     */
    private Map<String, ConversionStretagy> convertors = new HashMap<>();

    /**
     * 系统内置的类型转换器
     */
    private static final String[] INNER_CONVERTOR_CLASS = new String[]{
            "com.hsc.mvc.conversion.FileArray2FileConvertor",
            "com.hsc.mvc.conversion.StringArray2DoubleConvertor",
            "com.hsc.mvc.conversion.StringArray2IntArrayConvertor",
            "com.hsc.mvc.conversion.StringArray2IntegerArrayConvertor",
            "com.hsc.mvc.conversion.StringArray2IntegerConvertor",
            "com.hsc.mvc.conversion.StringArray2LongConvertor",
            "com.hsc.mvc.conversion.StringArray2StringConvertor",
    };

    private static final String[] INNER_HANDLER_CLASS = new String[]{
            "com.hsc.mvc.handler.StringResultHandler", // 先把处理@RequestBody 的处理器先放入
            "com.hsc.mvc.handler.ObjectResultHandler", // 处理json字符串的处理器
            "com.hsc.mvc.handler.RedirectHandler", // 进行重定向的处理器
            "com.hsc.mvc.handler.ForwardHandler", // 进行转发的处理放在尾部
            "com.hsc.mvc.handler.ModelAndViewHandler"
    };

    private List<ResponseHandleStrategy> handlers = new ArrayList<>();


    /**
     * 处理所有的类型转换器信息 项目启动的时候将会初始化
     */
    private List<ConvertorTag> convertorTags = new ArrayList<>();

    /**
     * 读取配置文件方式
     * 不同的配置文件 读取的方式不同
     * 使用策略设计模式 针对不同的读取方式 提供不同的实现类
     *
     * @param configLocation
     */
    public void readXml(String configLocation) {
        if (configLocation.startsWith("classpath:")) {
            //基于classpath路径的xml
            // Configuration 我们只需要一份即可 所有我们将this 转递
            new ClasspathXMLReader(this).read(configLocation);
        } else if (configLocation.startsWith("http:")) {
            //远程xml
            new RemoteXMLReader(this).read(configLocation);
        } else {
            //基于system路径的xml
            new SystemXMLReader(this).read(configLocation);
        }
    }

    /**
     * 读取注解方式
     * 读取注解的方式 是 扫描包下的所有类
     * <init-param>
     * <param-name>configPackage</param-name>
     * <param-value>
     * com.controller,
     * com.interceptor,
     * com.service,
     * com.dao
     * </param-value>
     * </init-param>
     * 然后解析类上的注解
     *
     * @param configPackage
     */
    public void readAnnotation(String configPackage) {
        new AnnotationReader(this).read(configPackage);
    }

    public void putMapping(MappingTag mapping) {
        String nameStr = mapping.getNameStr();
        if (mappingMap.get(nameStr) != null) {
            //证明当前这个url，之前已经有mapping标签，配置过了
            //重复配置，不允许，抛出异常
            throw new RepeatMappingException(nameStr);
        }
        mappingMap.put(nameStr, mapping);
    }

    public void putAopMapping(AopMappingTag aopTag) {
        aopMappingTagList.add(aopTag);
    }

    /**
     * 获取代理对象 里面包含目标对象和切面对象
     *
     * @param requestURI
     * @return
     */
    public ExecutorProxy getExecuteProxy(String requestURI) {
        // 从controller 请求映射对象集合中获取对应的 处理对象信息
        MappingTag mappingTag = mappingMap.get(requestURI);
        // 如果对象为null 说明有可能是 文件资源 如何 HTML  或者警jsp 文件资源 交给tomcat的处理器处理
        if (mappingTag == null) {
            return null;
        }
        // 创建一个代理对象
        ExecutorProxy executorProxy = new ExecutorProxy(this);
        // 设置对应的处理器对象 目标对象 也就是要执行的controller
        executorProxy.setTarget(mappingTag);
        List<AopMappingTag> aopMappingTags = new LinkedList<>();
        // 获取所有切面中 符合url的切面对象 并且设置保存在代理执行对象中
        for (AopMappingTag aopMappingTag : aopMappingTagList) {
            // 封装一个方法判断当前的切面对象是否符合要求 进行拦截的
            if (aopMappingTag.isSupport(requestURI)) {
                // 满足拦截要求 进行保存到容器
                aopMappingTags.add(aopMappingTag);
            }
        }
        // 将请求映射对象设置打代理对象中
        executorProxy.setInterceptors(aopMappingTags);
        return executorProxy;
    }

    public List<ParameterBindStrategy> getBinders() {
        return this.binders;
    }

    /**
     * 初始化参数绑定器对象
     */
    public void initParameterBinder() throws InstantiationException, IllegalAccessException {
        //目前框架有3个绑定器， 使用者提供了一个（多个）绑定器。
        //应该使用list集合存储绑定器对象
        //需要考虑绑定器存储的顺序,其中实体绑定器应该是最后。
        //  当前面的绑定器都失效时就认为应该是实体绑定器了
        //  所以我们将使用者自定义的绑定器，按照配置顺序放在前面
        //  将框架内置的绑定器放在后面
        //  当然，也可以为每一个绑定器增加一个序号。 order
        // 将自定义的参数绑定器以及 系统的参数绑定器进行初始化
        for (ParamBinderTag paramBinderTag : paramBinderTags) {
            ParameterBindStrategy parameterBindStrategy = (ParameterBindStrategy) paramBinderTag.getBinderClass().newInstance();
            binders.add(parameterBindStrategy);
        }
        // 创建系统内置的参数绑定器
        binders.add(new NormalParameterBinder());
        binders.add(new ServletParameterBinder());
        // 我们将系统实体类的参数绑定器放到最后
        binders.add(new EntityParameterBinder());

    }

    /**
     * 存储自定义的参数绑定器对象信息
     *
     * @param paramBinderTag
     */
    public void addBinder(ParamBinderTag paramBinderTag) {
        paramBinderTags.add(paramBinderTag);
    }


    /**
     * 获取转换器对象（具体的策略类）
     *
     * @param key
     * @return
     */
    public ConversionStretagy getConvertor(String key) {
        return convertors.get(key);
    }

    /**
     * 初始化类型转换器 <br/>
     * 初始化后的类型转换器存储在哪里呢？  list？ map?呢 <br/>
     * 分析可知，类型转换器有明确的查找条件，原始数据类型 + 目标类型的组合<br/>
     * 所以可以使用map存储<br/>
     * key = "原始数据类型-目标数据类型" <br/>
     * key = "java.lang.String[]-java.lang.String" <br/>
     * = "java.lang.String[]-java.lang.Integer[]" <br/>
     */
    public void initConvertor() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 创建框架内置的类型转换器
        // 用户自定义的类型转换器可以覆盖内置的转换器
        // 框架内置的类型转换器类的信息我们事先定义好 INNER_CONVERTOR_CLASS
        for (String innerConvertorClass : INNER_CONVERTOR_CLASS) {
            // 反射获取到具体的类
            Class<?> convertorClass = Class.forName(innerConvertorClass);
            // 实例化
            ConversionStretagy conversionStretagy = (ConversionStretagy) convertorClass.getConstructor().newInstance();
            // 获取key  key的设计：  原始数据类型-目标数据类型 =》 key = "java.lang.String[]-java.lang.String"
            String key = getConvertorKey(convertorClass);
            // 将key 和对应的策略类 绑定存储
            this.convertors.put(key, conversionStretagy);
        }
        // 初始化用户自定义的类型转换器 这里我们也是支持注解和xml配置 给用户来定义类型转换器
        // 我们提供的标签为 ConvertorTag 这些标签会跟 参数绑定器 拦截器 映射处理器 一起进行初始化
        for (ConvertorTag convertorTag : convertorTags) {
            ConversionStretagy c = (ConversionStretagy) convertorTag.getConvertorClass().getConstructor().newInstance();
            String key = getConvertorKey(convertorTag.getConvertorClass());
            this.convertors.put(key, c);
        }

        // 初始化完成 设置到工具类中
        ConversionUtil.setConfiguration(this);

    }

    /**
     * 获取转换器的key
     * 由于我们规定接口的规范为泛型形式 ConversionStretagy<L,R>
     * 如何获取器的key ，通过类对象的信息 获取当前类对象的接口 接口信息中获取原始数据类型和目标数据类型
     *
     * @param convertorClass
     * @return
     */
    private String getConvertorKey(Class<?> convertorClass) {
        //convertorClass==StringArray2StringConvertor
        //public class StringArray2StringConvertor implements ConversionStretagy<String[],String>
        //找到接口中的两个泛型
        // 获取当前类对象的父类接口信息 注意 我们需要获取到包括父类接口中的参数信息
        Type[] genericInterfaces = convertorClass.getGenericInterfaces();
        for (Type genericInterface : genericInterfaces) {
            // 判断接口类型是否为ConversionStretagy 类型的 ，有可能一个类会实现多个接口
            if (genericInterface.getTypeName().startsWith(ConversionStretagy.class.getName())) {
                // 说明是当前的策略类接口
                // 截取接口中的泛型参数 com.hsc.mvc.conversion.ConversionStretagy<java.lang.String[], java.lang.String>
                String typeName = genericInterface.getTypeName();
                int left = typeName.indexOf("<");
                int right = typeName.indexOf(">");
                String generic = typeName.substring(left + 1, right);
                // 替换空格 并且拼凑key java.lang.String[], java.lang.String
                String key = generic.replace(" ", "").replace(",", "-");
                return key;
            }
        }
        // 将参数转为key
        return null;
    }

    public void addConvertor(ConvertorTag convertorTag) {
        convertorTags.add(convertorTag);
    }

    public Object getSingleObject(Class<?> mappingClass) {
        return singleObjectMap.get(mappingClass);
    }

    public boolean isExist(Class<?> key) {
        return singleObjectMap.containsKey(key);
    }

    public void putSingleObject(Class<?> key, Object singleObject) {
        singleObjectMap.put(key, singleObject);
    }

    public void initResponseHandler() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 优先使用自定义的响应处理器
        // 创建内置的响应器
        for (String innerHandlerClass : INNER_HANDLER_CLASS) {
            Class<?> handlerClass =Class.forName(innerHandlerClass);
            ResponseHandleStrategy responseHandleStrategy = (ResponseHandleStrategy) handlerClass.getConstructor().newInstance();
            handlers.add(responseHandleStrategy);
        }

    }

    public List<ResponseHandleStrategy> getHandlers() {
        return handlers;
    }
}
